#pragma once
#include <iostream>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <fstream>
#include "LockGuard.hpp"

bool gIsSave = false;
const std::string logName = "log.txt";

enum Level
{
    DEBUG = 0,
    INFO,
    WARNING,
    ERROR,
    FATAL
};

std::string GetLevelString(int level)
{
    switch(level)
    {
        case DEBUG: return "Debug";
        case INFO: return "Info";
        case WARNING: return "Warning";
        case ERROR: return "Error";
        case FATAL: return "Fatal";
        default: return "Unknown";
    }
}

std::string GetTimeString()
{
    time_t currTime = time(nullptr);
    struct tm* tm = localtime(&currTime);
    char timeBuffer[1024];
    snprintf(timeBuffer, sizeof(timeBuffer) - 1, "%d-%02d-%02d %02d:%02d:%02d",
        tm->tm_year + 1900,
        tm->tm_mon + 1,
        tm->tm_mday,
        tm->tm_hour,
        tm->tm_min,
        tm->tm_sec
        );
    return timeBuffer;
}

pthread_mutex_t gLock = PTHREAD_MUTEX_INITIALIZER;

void SaveFile(const std::string& logName, const std::string& message)
{
    std::ofstream ofs(logName, std::ios::app);
    if(!ofs.is_open()) return;

    ofs << message;
    ofs.close();
}

void LogPrint(std::string fileName, int line, bool isSave, int level, const char* format, ...)
{
    std::string levelStr = GetLevelString(level);
    std::string timeStr = GetTimeString();
    pid_t selfID = getpid();

    va_list arg;
    va_start(arg, format);
    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer) - 1, format, arg);
    va_end(arg);
    std::string message = "[" + timeStr + "]" + "[" + levelStr + "]" + "[PID:" + std::to_string(selfID) + "]" + "[" + fileName + "]" + "[" + std::to_string(line) + "] " + buffer;

    LockGuard lock(&gLock);
    if(!isSave)
    {
        std::cout << message;
    }
    else
    {
        SaveFile(logName, message);
    }
}

#define LOG(level, format, ...) do{ LogPrint(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); } while(0)
#define EnableFile() do{ gIsSave = true; } while(0)
#define EnableScreen() do{ gIsSave = false; } while(0)
